In order to correct problems in code under development, software developers often use a debugging tool. In order to be effective, a debugging tool provides information which assists a developer in determining where an identified problem has occurred. This allows the developer to correct the problem.
One way in which debugging tools provide information to a user is via an analysis of the thread stack. The thread stack contains a call stack, with data related to the function calls executed during the execution of a program. The thread stack also contains other data like local variables and parameters. The thread stack, used, for example, in the x86 architecture, contains a stack frame for each function call. The stack frame includes information about the function, including, e.g., function parameters, function return address, and locally declared variables and buffers.
When a function is called, information (e.g. a stack frame) regarding the function is pushed onto the thread stack. When the function returns, information is removed from the thread stack and the register trace. The register trace shows the value of registers at the point of the crash. One such register is the EIP, also known as the program counter. Knowing the EIP allows one to look at the machine instruction causing the failure. Since the instruction possibly uses other registers, the value of all registers can be used to determine out the specific nature of the crash. Thus, debugger users use the information in the call stack and information from a register trace performed by the debugger in order to determine where the problem has occurred.
One cause for failure in the execution of a program is a buffer overflow. One of the locally declared variables and buffers in the stack frame may be written with more information than the size of the buffer. This affects the values in other buffers, which causes errors or allows a malicious function to change function data, for example altering the execution path of a program.
Some compilers, for example Microsoft's Visual C++.NET compiler, allow for easier detection of such buffer overrun problems via a “speed bump” or “security cookie”. In such compilers, a special “security cookie” value is stored in the call stack in a location which will allow detection of a buffer overrun. If the security cookie has been altered in any way, this indicates that an overrun has happened and that data has been compromised. A compiler may allow the insertion of such cookies in the compiled code via a compile switch. The “/GS” compile switch in Microsoft Corporation's Visual C++.Net is used to indicate that security cookies should be used.
On function entry, the space allocated for the security cookie is loaded with a security cookie that is computed once at module load. Then, on function exit, a helper is called to make sure the cookie's value is still valid. If they are different, then a problem has been detected. The cookie is compared with a stored copy of the cookie to determine validity.
The security cookie is used to determine that a failure has occurred. However, a current limitation of debuggers is in their limited use of static analysis in determining the cause of failures. As discussed above, a register trace and a call stack are generally available to the debugger and a fair amount of information can be collected using merely these. However, the call stack only points out the direct failure path. This may not include the information necessary to determine what function caused the failure.
For example, when a failure occurs, the call stack may contain information regarding two functions, A and B. However, this does not mean that the failure is attributable to one of these two functions. When these two functions appear on the call stack, they have been called and not returned from. However, it is possible that many other functions could have been called and returned from, and the root cause of the failure could likely lie inside a function that is thus not even present in the call stack. Simple examination of the call stack will not yield information regarding functions which have been called and returned from, and thus do not appear on the call stack.
When a security cookie has been corrupted, determining the source of the corruption is important in order to remediate the problem which resulted in the corruption. During a crash, an application reporting tool enables the generation of what is called a “crash dump” containing information about the crash. There are different types of crash dumps and the type of dumps collected and sent depends on how the user has the system configured. Sometimes the address and value of the corrupted security cookie will be available in the crash dump. The address and/or value may not be available if the stack frame is corrupted, for example, which is likely when a buffer overrun occurs. In any case, merely having the address and value of the security cookie does not provide insight into the function which caused the corruption.
Debugging also often requires the source code of the programs. For various reasons, this source code may not always be available. For example, when using a standard library for a function, the source code may not be available for a function, which may complicate debugging.